home *** CD-ROM | disk | FTP | other *** search
- /*
- * m c . c
- *
- * Multi-column filter
- *
- */
-
- /*)BUILD
- $(TKBOPTIONS) = {
- TASK = ...MCX
- UNITS = 10
- ACTFIL = 10
- }
- */
-
- /*
- )EDITLEVEL=40
- * Edit history
- * 0.0 ??-???-?? ??? Original implementation distributed with DECUS C.
- * 1.0 19-May-81 JSL Extensive reorganization; added -t option, balancing
- * of columns on final page. Bugfix: Don't put out a
- * blank initial item.
- * 2.0 20-Jul-82 JSL Converted to tool standards.
- * 2.1 23-Jul-82 JSL Added -g switch.
- * 2.2 27-Jul-82 JSL Added multiple-file handling.
- * 2.3 1-Aug-82 JSL Redid overstrike handling; much more extensive error
- * and bounds checks; debug conditionally compiled.
- * 2.4 ??-Aug-82 MM Change default page height to 60
- * 2.5 23-Sep-82 JSL Added conditional code for VAX-11 C
- * 2.6 11-apr-85 SCP Ported to ms-DOS DESMET 'C' removed piping
- * capability for performance, removed space between opt and #
- */
-
- char *documentation[] = {
- " mc [-ccolumns] [-hheight] [-ggutter] [-wwidth]",
- " [filespec | aliasspec]... outfile",
- "",
- "mc reads one or more files and converts them to a single multi-column file",
- "that it writes to an output file. ",
- "",
- "Alias specifications provide a method for controlling the placement of file",
- "data. An alias specification is a reference to another file (or alias)",
- "specification. File and alias specifications are numbered, starting at one",
- "for the left-most such specification; switches and their arguments do not",
- "affect the numbering. The alias specification #n indicates that the n'th",
- "specification is to be repeated. Such a specification is legal only if it",
- "refers to an earlier specification.",
- "",
- "mc has two modes of operation:",
- " one input file - the file is presented in multicolumn format,",
- " records may be truncated to fit.",
- " multiple inputs - each input file (or alais) will occupy one column ",
- " on the output. Files should be of equal length.",
- "",
- "The following switches are available:",
- " -c Number of columns default - 2",
- " -h Height, in lines, of a page default - 58",
- " -g Gutter width (the space between columns) default - 1",
- " -w Width, in characters, of a page default - 80",
- "",
- " mc [-c2] [-h58] [-g1] [-w80]",
- " [filespec | aliasspec]... outfile",
- 0 };
-
- #ifdef vax11c
- #include ctype.h
- #include stdio.h
- #define exits(x) exit(x)
- #define IO_ERROR 2
- #define FALSE 0
- #define TRUE 1
- #define EOS 0
- #else
- #include <stdio.h>
- #endif
-
- /*
- * Turn on to include debugging code
- */
- /* #define DEBUG */
-
- #define LINEMAX 256 /* Maximum line length handled */
- /* (also maximum column width) */
- #define NFILES 10 /* Maximum files (including */
- /* aliased files) */
- #define ALIAS '#' /* Marks an alias argument */
- /* (Can't be "-") */
- #ifdef DEBUG
- int debug = 0;
- #endif
-
- int NEWLN = 3338;
-
- int columns = -1; /* All these will be given */
- int gutter = -1; /* default values later unless */
- int height = -1; /* the user sets them first */
- int width = -1; /* (to a positive value!) */
-
- int pause = FALSE; /* Pause-at-end of page flag */
- int first = TRUE; /* First-time-through flag */
- int cwidth; /* Total (column+gutter) width */
- int pagesize; /* Total bytes in page */
- int nf; /* Number of file & alias specs */
- int files = 0; /* Number of files still open */
- int aliases = 0; /* Number of alias specs */
-
- FILE file[NFILES]; /* File pointers for our files */
- FILE outfp; /* SP - and rightmost is output file */
- char line[LINEMAX]; /* Input line buffer */
- int linelen; /* Length of a line in line[] */
- int lineend; /* Last usable line[] position */
- char *page; /* -> page paste-up matrix */
-
- main(argc,argv)
- int argc;
- char *argv[];
- { register char *p;
- register int c,i;
- int n;
- long PAGE;
- FILE fp;
-
- if (argc < 2) usage();
- if (argc == 2 && argv[1][0] == '?' && strlen(argv[1]) == 1)
- { help();
- return;
- }
- --argc; /* SP---*/
- nf = argc - 1;
- for (i = 1; i < argc; i++) {
- if (argv[i][0] != '-') break;
- --nf;
- c = argv[i][1];
- switch(tolower(c))
- {
- #ifdef DEBUG
- case 'd': debug++; break;
- #endif
- case 'c':
- columns = atoi(&argv[i][2]);
- if (columns <= 0) bad("Columns");
- break;
-
- case 'g':
- gutter = atoi(&argv[i][2]);
- if (gutter <= 0) bad("Gutter");
- break;
-
- case 'h':
- height = atoi(&argv[i][2]);
- if (height <= 0) bad("Height");
- break;
-
- case 'w':
- width = atoi(&argv[i][2]);
- if (width <= 0) bad("Width");
- break;
-
- default:
- usage();
- break;
- }
-
- }
-
-
- if (nf == 0)
- {
- error("Too few files");
- }
- else
- for ( ; i < argc ; i++)
- if (p = argv[i])
- switch (*p)
- {
- case ALIAS:
- n = atoi(&p[1]) - 1;
- if (n < 0 || n >= files) usage("File");
- file[files++] = file[n];
- aliases++;
- break;
-
- default:
- if ((fp = fopen(p,"r")) == NULL)
- { fprintf(stderr,
- "%s: ",p);
- error("Can't open");
- }
- #ifdef DEBUG
- fprintf(stderr,"\nOpened input: %s",p);
- #endif
- file[files++] = fp;
- break;
- }
- if (nf > NFILES )
- error("Too many file and alias arguments, or bad parm order");
-
- if ((outfp = fopen(argv[argc],"w")) == NULL)
- { fprintf(stderr, "%s: ",argv[i]);
- error("Can't open"); }
- #ifdef DEBUG
- fprintf(stderr,"\nOpened output: %s",argv[i]);
- #endif
- files -= aliases; /* Aliases aren't open */
-
- /*
- * Establish defaults for any parameters the user didn't set
- */
- if (width < 0) width = 80;
- if (gutter < 0) gutter = 1;
- if (height < 0) height = 58;
- if (columns < 0)
- if (nf > 1)
- columns = nf;
- else
- columns = 2;
-
- /*
- * The last column isn't followed by a gutter, but dealing with this makes
- * the computation too complex; so we simply pretend the page is wider, which
- * is ok since the code trims the trailing spaces that would go there anyway.
- * This is, of course, quite wasteful of space, but then so is the whole algo-
- * rithm; we shouldn't be storing ANY of the gutters explicitly.
- */
- width += gutter;
- cwidth = width/columns;
-
- if (cwidth <= gutter || (cwidth - gutter) > LINEMAX)
- error("Unreasonable -c/-g/-w combination\n");
-
- lineend = line + (cwidth - gutter);
-
- PAGE = ( long) height * (width + gutter);
- if (PAGE > 30000L)
- error("Insufficient memory - sorry\n");
- page = calloc(pagesize = (int) PAGE);
- if (page == NULL )
- error("Insufficient memory - sorry\n");
-
- #ifdef DEBUG
- if (debug)
- { fprintf(stderr,"width %d, height %d, columns %d, cwidth %d\n",
- width,height,columns,cwidth);
- fprintf(stderr,
- "\tgutter %d, pause %d, pagesize %d, page at 0%o\n",
- gutter,pause,pagesize,page);
- fprintf(stderr,"%d files(%d real + %d aliases)\n",
- nf,files,aliases); }
- #endif
-
- process();
- fclose(outfp);
- free(page);
- }
-
- /*
- * Process all the data
- */
- process()
- { register int offset; /* Offset into page */
- register int items; /* Counts items added */
- register int maxitems; /* Room for this many */
- int curfile; /* Current file */
-
- maxitems = columns * height;
- fputc('\f',outfp);
- blank();
- curfile = items = offset = 0;
- while (get(file[curfile]))
- { if (items >= maxitems)
- { output(items);
- blank();
- items = offset = 0;
- }
- #ifdef DEBUG
- if (debug > 3)
- fprintf(stderr,"Inserting %s at offset %d, file %d\n",
- line,offset,curfile);
- #endif
- strncpy(page+offset,line,linelen);
- items++;
- if ((items % height) == 0) /* Bottom of a column */
- { curfile = (curfile + 1) % nf;
- #ifdef DEBUG
- if (debug > 1)
- fprintf(stderr,"Switching to file %d of %d\n",
- curfile,files);
- #endif
- }
- offset += cwidth;
- }
- output(items);
- }
-
- /*
- * Print out the buffered page, which has been filled with items items.
- */
- output(items)
- int items; /* # of items the caller used */
- { int nrows;
- register int i,col,row;
-
- #ifdef DEBUG
- if (debug) fprintf(stderr,"output items(%d), nf(%d)\n",items,nf);
- #endif
- if (items <= 0) /* Nothin' to do */
- return;
-
- /*
- * Get number of rows we'll need. This is the basis of the "last page"
- * optimization - we don't use all the rows, just enough to hold everything
- * (items/columns, rounded up). If there's more than are one file, just use
- * the whole page.
- */
- if (nf == 1) nrows = (items + (columns - 1)) / columns;
- else nrows = height;
-
- #ifdef DEBUG
- if (debug > 1)
- fprintf(stderr,"items %d, nrows %d\n",items,nrows);
- if (debug > 2)
- { page[pagesize] = 0;
- fprintf(stderr,"Dump of page:\n%s\n",page);
- }
- #endif
-
- if (first)
- first = FALSE;
- else
- { if (pause)
- { printf("\t\t\t Type CTRL/Z to exit, any other key to continue...");
- fflush(stdout);
- i = ci();
- fwrite(&NEWLN,1,2,outfp);
- if (i == 26) /* CTRL/Z */
- exit();
- }
- fputc('\f',outfp);
- }
-
- /*
- * Scan through page[] row-wise, after having filled it column-wise. (Page[]
- * is laid out column-wise in memory.)
- */
- for (row = 0; row < nrows; row++)
- { for (col = 0; col < columns; col++)
- putitem(page+(row+col*nrows)*cwidth,
- (col == columns - 1));
- fwrite(&NEWLN,1,2,outfp);
- }
- }
-
- /*
- * Put out one item, possibly trimming trailing spaces
- */
- putitem(base,trim)
- register char *base; /* First char to put */
- int trim; /* Trim trailing spaces */
- { register char *end; /* End of item */
- int len;
-
- end = &base[cwidth - 1];
- if (trim)
- while (*end == ' ')
- --end;
- len = end - base;
- #ifdef DEBUG
- if (len > 256)
- puts("\n wow -excessive length");
- #endif
- if (len > 0)
- fwrite(base,1,len + 1,outfp);
- }
-
- /* * Blank out page[] */
-
- blank()
- { register int n;
-
- for (n = 0; n < pagesize;)
- page[n++] = ' ';
- }
-
- /* * Fill line[]; return FALSE when all files have reached EOF, TRUE until then.
- */
- get(fp)
- FILE fp;
- { register char *p; /* Current char pos */
- register char *high; /* Char pos high water */
- register int c; /* Character */
- char tempc;
-
- if ((tempc = getc(fp)) == -1)
- { linelen = 0; /* Pretend we read "" */
- return(TRUE);
- }
- ungetc(tempc,fp);
-
- high = p = line;
- while ((c = getc(fp)) != EOF && c != '\n')
- switch(c)
- {
- case '\b':
- if (p > line)
- --p;
- break;
-
- case '\r':
- p = line;
- break;
-
- case '\t':
- if (((p - line) & 07) != 07)
- ungetc(c,fp);
- c = ' ';
- /*
- * Fall through...
- */
- default:
- if (isprint(c))
- { if ((p < lineend)
- && (p == high || *p == '_' || *p == ' '))
- { *p = c;
- if (p == high)
- high++;
- }
- p++;
- }
- break;
- }
-
- linelen = high - line;
-
- if (c != EOF)
- return(TRUE);
- else
- return((--files != 0));
- }
-
- bad(s)
- char *s;
- { printf("\n %s specification bad",s); usage(); }
-
- usage()
- {
- fprintf(stderr,"\nUsage:\n mc [-ccolumns] [-ggutter] ");
- puts("[-hheight] [-wwidth] [infile(s) | #n]... outfile\n");
- error("\n mc ? for more help");
- }
-
- help()
- /*
- * Give good help
- */
- { register char **dp;
-
- for (dp = documentation; *dp; dp++)
- printf("%s\n",*dp);
- }
-
- error(str)
- char *str;
- { puts(str);
- exit(1);
- }